<?php
namespace Apps\Weixin\Services;


use Apps\Common\Constant\BusinessConstant;
use Apps\Common\Constant\CacheKeyConstant;
use Apps\Common\Constant\CacheTimeoutConstant;
use Apps\Common\Service\BaseService;
use Apps\Weixin\Models\WxAuthInfo;
use Deyi\Functions\CacheFunction;
use Deyi\Functions\HttpFunction;

class WxResourceService extends BaseService
{
    const API_COMPONENT_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token';

    const API_PRE_AUTH_CODE_URL = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=';

    const API_QUERY_AUTH_URL = 'https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=';

    const API_AUTHORIZER_TOKEN_URL = 'https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=';

    public static $_authorizerAccessToken = [];

    public function getComponentAccessToken()
    {
        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_API_COMPONENT_TOKEN_CACHE_KEY, static::$_config->weixin->appid);

        $res = static::$_redis->get($cacheKey);

        if ($res && isset($res['expire'])) {
            if ((($res['expire'] - time()) < CacheTimeoutConstant::LOCAL_MAX_CACHE) && $this->serviceTransactionLock(__FUNCTION__)) {
                $res = $this->refreshComponentAccessToken();
                $this->serviceTransactionUnLock(__FUNCTION__);
            } else {
                $res = $res['value'];
            }
            return $res;
        }

        return $this->refreshComponentAccessToken();
    }

    public function refreshComponentAccessToken()
    {
        $res = WxRecordService::getInstance()->getValue(BusinessConstant::RECORD_SKEY_VERIFY_TICKET);
        if (!$res) {
            static::$_logger->error('ticket数据获取失败');
            return false;
        }

        $ret = HttpFunction::postDataToUrl(self::API_COMPONENT_TOKEN_URL, json_encode([
            'component_appid' => static::$_config->weixin->appid,
            'component_appsecret' => static::$_config->weixin->appsecret,
            'component_verify_ticket' => $res->sval
        ]), 5);

        if (!$ret || !($json = json_decode($ret)) || !isset($json->component_access_token)) {
            static::$_logger->error('component access token 数据获取失败, msg:' . $ret);
            return false;
        }

        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_API_COMPONENT_TOKEN_CACHE_KEY, static::$_config->weixin->appid);

        if (!static::$_redis->set($cacheKey, [
            'value' => $json->component_access_token,
            'expire' => (time() + $json->expires_in)
        ], $json->expires_in)) {
            static::$_logger->error('write redis access token fail');
        }

        WxRecordService::getInstance()->updateValue(BusinessConstant::RECORD_COMPONENT_ACCESS_TOKEN, $json->component_access_token);

        return $json->component_access_token;
    }

    public function getPreAuthCode()
    {
        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_PRE_AUTHCODE_CACHE_KEY);

        $res = static::$_redis->get($cacheKey);

        if ($res) {
            return $res;
        }

        $url = self::API_PRE_AUTH_CODE_URL . $this->getComponentAccessToken();

        $ret = HttpFunction::postDataToUrl($url, json_encode([
            'component_appid' => static::$_config->weixin->appid
        ]), 5);

        if (!$ret || !($json = json_decode($ret)) || !isset($json->pre_auth_code)) {
            static::$_logger->error('pre authcode get fail: ' . $ret);
            return false;
        }

        static::$_redis->set($cacheKey, $json->pre_auth_code, ($json->expires_in - 100));

        WxRecordService::getInstance()->updateValue(BusinessConstant::RECORD_PRE_AUTHCODE, $json->pre_auth_code);

        return $json->pre_auth_code;
    }

    public function getAuthorizerInfo($authCode, $updCache = false)
    {
        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_AUTHORIZER_INFO_CACHE_KEY, $authCode);

        $res = $updCache ? false : static::$_redis->get($cacheKey);

        if ($res) {
            return $res;
        }

        $url = self::API_QUERY_AUTH_URL . $this->getComponentAccessToken();

        $ret = HttpFunction::postDataToUrl($url, json_encode([
            'component_appid' => static::$_config->weixin->appid,
            'authorization_code' => $authCode
        ]), 5);

        if (!$ret || !($json = json_decode($ret)) || !isset($json->authorization_info)) {
            static::$_logger->error('query authcode fail: ' . $ret);
            return false;
        }

        static::$_redis->set($cacheKey, $json, $json->expires_in);

        return $json;
    }


    public function updateAuthorizerInfo($authCode)
    {
        $ret = $this->getAuthorizerInfo($authCode, true);

        if (!$ret) {
            return false;
        }

        $authInfo = $ret->authorization_info;
        WxAuthInfo::getInstance()->insertByFields([
            'appid' => $authInfo->authorizer_appid,
            'access_token' => $authInfo->authorizer_access_token,
            'expires_in' => $authInfo->expires_in,
            'refresh_token' => $authInfo->authorizer_refresh_token,
            'func_info' => json_encode($authInfo->func_info),
            'dateline' => time()
        ], true);

        return $authInfo->authorizer_appid;
    }

    public function getAuthorizerAccessToken($authorizerAppId)
    {
        if ((RUN_MODE === 'WEB') && isset(static::$_authorizerAccessToken[$authorizerAppId])) {
            return static::$_authorizerAccessToken[$authorizerAppId];
        }

        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_ACCESSTOKEN_CACHE_KEY, $authorizerAppId);

        $res = static::$_redis->get($cacheKey);

        if ($res && isset($res['expire'])) {
            if ((($res['expire'] - time()) < CacheTimeoutConstant::LOCAL_MAX_CACHE) && $this->serviceTransactionLock(__FUNCTION__)) {
                $res = $this->refreshAuthorizerAccessToken($authorizerAppId);
                $this->serviceTransactionUnLock(__FUNCTION__);
            } else {
                $res = $res['value'];
            }
            return static::$_authorizerAccessToken[$authorizerAppId] = $res;
        }

        return static::$_authorizerAccessToken[$authorizerAppId] = $this->refreshAuthorizerAccessToken($authorizerAppId);
    }

    public function refreshAuthorizerAccessToken($authorizerAppId)
    {
        //获取refreshToken
        $res = WxAuthInfo::getInstance()->getOneByField('appid', $authorizerAppId);
        if (!$res) {
            return false;
        }

        $url = self::API_AUTHORIZER_TOKEN_URL . $this->getComponentAccessToken();

        $ret = HttpFunction::postDataToUrl($url, json_encode([
            'component_appid' => static::$_config->weixin->appid,
            'authorizer_appid' => $authorizerAppId,
            'authorizer_refresh_token' => $res->refresh_token
        ]), 5);

        if (!$ret || !($json = json_decode($ret)) || !isset($json->authorizer_access_token)) {
            static::$_logger->error('refresh authorizer accesstoken fail: ' . $ret);
            return false;
        }

        WxAuthInfo::getInstance()->updateByFields([
            'access_token' => $json->authorizer_access_token,
            'refresh_token' => $json->authorizer_refresh_token,
            'refresh_time' => time()
        ], [
            'appid' => $authorizerAppId
        ]);

        $cacheKey = CacheFunction::generateKey(CacheKeyConstant::WEIXIN_ACCESSTOKEN_CACHE_KEY, $authorizerAppId);

        static::$_redis->set($cacheKey, [
            'value' => $json->authorizer_access_token,
            'expire' => (time() + $json->expires_in)
        ], $json->expires_in);

        return $json->authorizer_access_token;
    }
}